import Vector2 from "../maths/Vector2";
import ECurveHitType from "../enums/EcurveHitType";
import CanvasMouseEvent from "../events/CanvasMouseEvent";
import Math2D from "../maths/Math2D";

export default class QuadraticBezierCurve {
    // 受保护的成员变量，能够被继承的子类访问
    protected _startAnchorPoint: Vector2;   // 起点，相当于p0点
    protected _endAnchorPoint: Vector2;     // 终点，相当于p2点
    protected _controlPoint0: Vector2;      // 控制点,相当于p1点

    protected _drawLine: boolean;   // 是否要绘制连线，方块表示的锚点和原点表示的控制点
    protected _lineColor: string;   // 绘制线段的颜色
    protected _lineWidth: number;   // 绘制的线宽
    protected _radiusOrLen: number; //方块表示的锚点和原点表示的控制点的大小

    protected _drawSteps: number;
    protected _points !: Array<Vector2>;
    protected _showCurvePt: boolean;
    protected _dirty: boolean;

    protected _hitType: ECurveHitType;

    public constructor(start: Vector2, control: Vector2, end: Vector2, drawSteps: number = 30) {
        // 初始化控制点
        this._startAnchorPoint = start;
        this._endAnchorPoint = end;
        this._controlPoint0 = control;
        this._drawSteps = drawSteps;
        // 初始化渲染属性
        this._drawLine = true;
        this._lineColor = 'black';
        this._lineWidth = 1;
        this._radiusOrLen = 5;
        this._dirty = true;
        this._showCurvePt = true;

        this._hitType = ECurveHitType.NONE;
    }

    public set dirty(t: boolean) {
        this._dirty = true;
    }

    public get dirty(): boolean {
        return this._dirty = true;
    }

    public get startPoint(): Vector2 {
        return this._startAnchorPoint;
    }

    public set startPoint(pt: Vector2) {
        this._startAnchorPoint = pt;
        this._dirty = true;
    }

    public get endPoint(): Vector2 {
        return this._endAnchorPoint;
    }

    public set endPoint(pt: Vector2) {
        this._endAnchorPoint = pt;
        this._dirty = true;
    }

    public get controlPoint0(): Vector2 {
        return this._controlPoint0;
    }

    public set controlPoint0(pt: Vector2) {
        this._controlPoint0 = pt;
        this._dirty = true;
    }
    private _calcDrawPoints(): void {
        if (this._dirty) {
            this._points = [];
            this._points.push(this._startAnchorPoint);
            let s: number = 1.0 / (this._drawSteps);
            for (let i = 1; i < this._drawSteps - 1; i++) {
                let pt: Vector2 = this.getPosition(s * i);
                this._points.push(pt);
            }
            this._points.push(this._endAnchorPoint);
            this._dirty = false;
        }
    }

    public update(): void {
        this._calcDrawPoints();
    }

    public onMouseDown(evt: CanvasMouseEvent): void {
        this._hitType = this.hitTest(evt.canvasPosition);
    }

    public onMouseUp(evt: CanvasMouseEvent): void {
        this._hitType = ECurveHitType.NONE;
    }
    public onMouseMove(evt: CanvasMouseEvent): void {
        if (this._hitType !== ECurveHitType.NONE) {
            switch (this._hitType) {
                case ECurveHitType.CONTROL_POINT0:
                    this._controlPoint0.x = evt.canvasPosition.x;
                    this._controlPoint0.y = evt.canvasPosition.y;
                    this._dirty = true;
                    break;

                case ECurveHitType.START_POINT:
                    this._startAnchorPoint.x = evt.canvasPosition.x;
                    this._startAnchorPoint.y = evt.canvasPosition.y;
                    this._dirty = true;
                    break;

                case ECurveHitType.END_POINT:
                    this._endAnchorPoint.x = evt.canvasPosition.x;
                    this._endAnchorPoint.y = evt.canvasPosition.y;
                    this._dirty = true;
                    break;
            }
        }
    }

    protected getPosition(t: number): Vector2 {
        if (t < 0 || t > 1.0) {
            throw new Error(" t的取值范围必须是[ 0 , 1 ]之间 ");
        }
        return Math2D.getQuadraticBezierVector(this._startAnchorPoint, this._controlPoint0, this._endAnchorPoint, t);
    }

    protected hitTest(pt: Vector2): ECurveHitType {
        if (Math2D.isPointInCircle(pt, this._controlPoint0, this._radiusOrLen)) {
            return ECurveHitType.CONTROL_POINT0;
        } else if (Math2D.isPointInRect(
            pt.x, pt.y,
            this._startAnchorPoint.x - (this._radiusOrLen + 5) * 0.5,
            this._startAnchorPoint.y - (this._radiusOrLen + 5) * 0.5,
            this._radiusOrLen + 5, this._radiusOrLen + 5)) {
            return ECurveHitType.START_POINT;
        } else if (Math2D.isPointInRect(
            pt.x, pt.y,
            this._endAnchorPoint.x - (this._radiusOrLen + 5) * 0.5,
            this._endAnchorPoint.y - (this._radiusOrLen + 5) * 0.5,
            this._radiusOrLen + 5, this._radiusOrLen + 5)) {
            return ECurveHitType.END_POINT;
        } else {
            return ECurveHitType.NONE;
        }
    }

    public toString(): string {
        return JSON.stringify(this, null, ' ');
    }
}